//import java.net.InetAddress;
//import java.net.UnknownHostException;
import java.io.IOException;
import java.util.ArrayList;

import jpcap.NetworkInterface;
import jpcap.packet.*;
import jpcap.*;

/**
 * Project: SecuritySnort
 * This class will take care of printing the collected IP Addresses.
 *
 * @author Jonathan Buttner
 * @version Mar 6, 2010
 */
public class ForceIPCmd extends Command
{
	/**************************************************************************
	 * 							Globals
	 *************************************************************************/
	private NetworkInterface _conInfo;
	
	/**************************************************************************
	 * 							Constructors
	 *************************************************************************/
	/**
	 * This constructor sets up the object.
	 * 
	 * @param device is the device being used to capture packets.
	 */
	public ForceIPCmd( NetworkInterface device )
	{
		_conInfo = device;
		_name = "forceipmac";
	}
	
	/**************************************************************************
	 * 							Public Methods
	 *************************************************************************/
	/**
	 * This method will scan computers connected to the network.
	 * 
	 * @param command is the tokenized version of the command given
	 * by the user.
	 */
	public void exeCommand( ArrayList < String > cmdline )
	{
		if ( cmdline.size() != 2 )
		{
			System.out.println( "The command 'forceipmac' must have a parameter!" );
			return;
		}
		String ip = cmdline.get( 1 );
		if ( ip.contains( "." ) )
		{
			_sendARPRequests( ip );
		}
		/*try
		{
			InetAddress iNet = InetAddress.getByName( cmdline.get(1) );
			
		}
		catch (UnknownHostException e)
		{*/
		else
		{
			char[] param = cmdline.get(1).toCharArray();
			if ( param.length > 4 )
			{
				System.out.println("Error: There is too many x's! Use at most 4." );
				return;
			}
			
			for ( int i = 0; i < param.length; i++ )
			{
				if ( param[i] != 'x' )
				{
					System.out.println( "Error: You put something"
							+ "other than an 'x' Use only 'x's" );
				}
			}
			
			_sendARPRequests( param.length );
		//}
		}
	}
	
	/**
	 * This method sets the connection device.
	 * 
	 * @param device is the connection device being used to send and receive
	 * packets.
	 */
	public void setDev( NetworkInterface device )
	{
		_conInfo = device;
	}
	
	/**************************************************************************
	 * 							Private Methods
	 *************************************************************************/
	/**
	 * This method is used to send ARP Requests for the given IP Addresses.
	 * 
	 * @param numX is the number of 256 values that need to be scanned.
	 */
	private void _sendARPRequests( int numX )
	{
		int first, sec, third, fourth;
		int keepFirst, keepSec, keepThird, keepFourth;
		first = sec = third = fourth = 255;
		
		byte[] scrip = null; 
		for (int i = 0; i < _conInfo.addresses.length; i++ )
		{
			if ( _conInfo.addresses[i].subnet == null )
				continue;
			else
			{
				scrip = _conInfo.addresses[i].address.getAddress();
				break;
			}
			//TODO This will introduce SO MANY bugs
		}
		if ( scrip == null )
			System.out.println( "ERROR: Could not retrieve user IP" );
		
		keepFirst = (int)scrip[0];
		keepSec = (int)scrip[1];
		keepThird = (int)scrip[2];
		keepFourth = (int)scrip[3];
		
		
		if ( numX == 1 )
		{
			fourth = keepFourth = 0;
			scrip[3] = (byte)0;
		}
		else if ( numX == 2 )
		{
			fourth = third = 0;
			keepFourth = keepThird = 0;
			scrip[3] = (byte)0;
			scrip[2] = (byte)0;
		}
		else if ( numX == 3 )
		{
			fourth = third = sec = 0;
			keepFourth = keepThird = keepSec = 0;
			scrip[3] = (byte)0;
			scrip[2] = (byte)0;
			scrip[1] = (byte)0;
		}
		else
		{
			fourth = third = sec = first = 0;
			keepFourth = keepThird = keepSec = keepFirst = 0;
			scrip[3] = (byte)0;
			scrip[2] = (byte)0;
			scrip[1] = (byte)0;
			scrip[0] = (byte)0;
		}
		
		//This really stinks, find another cleaner solution!
		for (; first < 256; first++ )
		{
			for (; sec < 256; sec++ )
			{
				for (; third < 256; third++ )
				{
					for (; fourth < 256; fourth++ )
					{
						scrip[0] = (byte)keepFirst;
						scrip[1] = (byte)keepSec;
						scrip[2] = (byte)keepThird;
						scrip[3] = (byte)keepFourth;
						ARPPacket arp = _assembleARP( scrip );
						try
						{
							JpcapSender sender = JpcapSender.openDevice( _conInfo );
							sender.sendPacket( arp );
							//System.out.println( "REQUEST SENT!" );
						} 
						catch (IOException e)
						{
							System.out.println( "Error openning the device!"
									+ "Command did not execute!" );
							return;
						}
						keepFourth++;
					}
					keepThird++;
				}
				keepSec++;
			}
			keepFirst++;
		}
	}
	
	/**
	 * This method is used to send an ARP Request to the given IP Address.
	 * 
	 * @param iNet is the InetAddress that contains the IP Address.
	 */
	private void _sendARPRequests( String ip )
	{
		String[] brokenIP = ip.split("\\.");
		if ( brokenIP.length != 4 )
		{
			System.out.println( "Malformed IP Address! The"
					+ "command did not execute!" );
			return;
		}
		else
		{
			byte first = (byte)Integer.parseInt( brokenIP[0] );
			byte sec = (byte)Integer.parseInt( brokenIP[1] );
			byte third = (byte)Integer.parseInt( brokenIP[2] );
			byte fourth = (byte)Integer.parseInt( brokenIP[3] );
			byte[] destIP = { first, sec, third, fourth };
			ARPPacket arp = _assembleARP( destIP );
			if ( arp == null )
				return;
			try
			{
				JpcapSender sender = JpcapSender.openDevice( _conInfo );
				sender.sendPacket( arp );
				//System.out.println( "REQUEST SENT!" );
			} 
			catch (IOException e)
			{
				System.out.println( "Error openning the device!"
						+ "Command did not execute!" );
				return;
			}
		}
	}
	
	private ARPPacket _assembleARP( byte[] ip )
	{
		byte[] macDes = new byte[] { (byte) 255, (byte) 255,
				(byte) 255, (byte) 255, (byte) 255, (byte) 255 };
		//NOTE TO SELF the unknown mac HAS to be all F's not 0's!!!!
		byte[] scrip = null; 
		for (int i = 0; i < _conInfo.addresses.length; i++ )
		{
			if ( _conInfo.addresses[i].subnet == null )
				continue;
			else
			{
				scrip = _conInfo.addresses[i].address.getAddress();
				break;
			}
			//TODO This will introduce SO MANY bugs
		}
		if ( scrip == null )
		{
			System.out.println( "ERROR: Could not retrieve user IP" );
			return null;
		}
		
		byte[] destip = ip;

		// Creates the ARP reply packet
		ARPPacket arp = new ARPPacket();
		// Set the parameters of the ARP packet.
		arp.hardtype = ARPPacket.HARDTYPE_ETHER;
		arp.prototype = ARPPacket.PROTOTYPE_IP;
		arp.operation = ARPPacket.ARP_REQUEST;
		arp.hlen = 6; // Hardware address length
		arp.plen = 4; // Protocol address length
		arp.sender_hardaddr = _conInfo.mac_address;
		arp.sender_protoaddr = scrip;
		arp.target_hardaddr = macDes;
		arp.target_protoaddr = destip;
		// Create the Ethernet packet
		EthernetPacket ether = new EthernetPacket();
		ether.frametype = EthernetPacket.ETHERTYPE_ARP;
		ether.src_mac = _conInfo.mac_address;
		ether.dst_mac = macDes;
		arp.datalink = ether;
		return arp;
	}
}